#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/stat.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/wait.h>
#include <linux/list.h>
#include <linux/fsl_devices.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/vmalloc.h>
#include <linux/workqueue.h>
#include <linux/sizes.h>
#include <linux/types.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/uaccess.h>  // copy_to_user & copy_from_user 

static int major = 0;
static struct gpio_desc* sr04_trig;
static struct gpio_desc* sr04_echo;
static struct class *sr04_class;

ssize_t sr04_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
    int us;
    unsigned long flags;
    int timeout_us = 1000000;

    local_irq_save(flags);              //关中断并保存

    /* 10us高电平 */
    gpiod_set_value(sr04_trig, 1);
    udelay(10);
    gpiod_set_value(sr04_trig, 0);

    while(!gpiod_get_value(sr04_echo) && !timeout_us) {
        timeout_us--;
        udelay(1);
    }
    if(!timeout_us) {
        local_irq_restore(flags);
        return -1;
    }

    timeout_us = 1000000;
    while (gpiod_get_value(sr04_echo) && !timeout_us) {
        udelay(1);
        timeout_us--;
        us++;
    }
    if(!timeout_us) {
        local_irq_restore(flags);
        return -1;
    }
    local_irq_restore(flags);
    copy_to_user(buf, &us, 4);

    return 4;
}

static struct file_operations sr04_file_op = {
    .owner = THIS_MODULE,
    .read  = sr04_read,
};

static const struct of_device_id sr04_id[] = {
    { .compatible = "PB, sr04"},
    { },
};

static int sr04_driver_probe(struct platform_device *pdev)
{       
    /* 从设备树获取硬件信息 */
    sr04_trig = gpiod_get(&pdev->dev, "trig", GPIOD_OUT_LOW);
    sr04_echo = gpiod_get(&pdev->dev, "echo", GPIOD_IN);
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    /* device_create */
    major = register_chrdev(0, "sr04", &sr04_file_op);
    sr04_class = class_create(THIS_MODULE, "sr04_CLASS");
    if (IS_ERR(sr04_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
        unregister_chrdev(major, "sr04");
        return -11;
    }
    device_create(sr04_class, NULL, MKDEV(major, 0), NULL, "sr04");

    return 0;
}

static int sr04_driver_remove(struct platform_device *pdev)
{
    device_destroy(sr04_class, MKDEV(major, 0));
    class_destroy(sr04_class);
    unregister_chrdev(major, "sr04");
    gpiod_put(sr04_trig);
    gpiod_put(sr04_echo);
    return 0;
}

static struct platform_driver sr04_driver_pdev = {
    .probe  = sr04_driver_probe,
    .remove = sr04_driver_remove,
    .driver = {
        .name = "sr04",
        .of_match_table = sr04_id,
     },
};

static int sr04_drv_init(void)
{
    platform_driver_register(&sr04_driver_pdev);
    return 0;
}

static void sr04_drv_exit(void)
{
    platform_driver_unregister(&sr04_driver_pdev);
}

module_init(sr04_drv_init);
module_exit(sr04_drv_exit);
MODULE_LICENSE("GPL");
